package com.ody.util.code;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.ody.util.code.generator.AbstractCodeGenerator;
import com.ody.util.code.generator.FileCodeGenerator;
import com.ody.util.code.generator.ICodeGenerator;
import com.ody.util.code.model.CachedModelProvider;
import com.ody.util.code.model.DatabaseModelProvider;
import com.ody.util.code.model.IModelProvider;
import java.awt.*;
import java.awt.event.*;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.PrintStream;
import java.net.URL;
import java.util.List;
import java.util.Map;
import javax.swing.*;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;

public class GeneratorGui {
	private JTextArea statusLine;
	private boolean renderModelProviderGui;
	
	public GeneratorGui() throws Exception {
		PipedInputStream pis = new PipedInputStream();
		PipedOutputStream pos = new PipedOutputStream(pis);
		System.setOut(new PrintStream(pos));
		
		BufferedReader br = new BufferedReader(new InputStreamReader(pis, "utf-8"));
		Thread t = new Thread(() -> {
			try {
				String line = null;
				while ((line = br.readLine()) != null) {
					statusLine.append("\n" + line);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		});
		t.setDaemon(true);
		t.start();
	}
	
	public void open(Map<String, ICodeGenerator> map, ApplicationContext ctx) throws Exception {
		JFrame.setDefaultLookAndFeelDecorated(true);
		JFrame frame = new JFrame(Const.CODE_GENERATOR_TITLE + Const.VERSION);
		JPanel root = new JPanel();
		root.setLayout(new BoxLayout(root, BoxLayout.PAGE_AXIS));
		root.setBackground(Color.LIGHT_GRAY);
		if (System.getProperty("os.name").contains("Window")) {
			UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
		}

		Toolkit toolkit = Toolkit.getDefaultToolkit();
		URL url = Main.class.getClassLoader().getResource("logo.png"); 
		frame.setIconImage(toolkit.createImage(url));
        
        if (renderModelProviderGui) {
            buildModelProviderGui(root, ctx);
        }

		List<String> enableGenerators = buildCodeGeneratorGui(root, map);

		JPanel btnPanel = new JPanel();
		btnPanel.setBackground(Color.GREEN);
		
		JButton ok = new JButton("确定");
		ok.addActionListener(t -> {
			try {
				String[] keys = map.keySet().toArray(new String[map.size()]);
                Map<String, ICodeGenerator> checkedGenerators = Maps.newHashMapWithExpectedSize(keys.length);
				for (String key : keys) {
					if (enableGenerators.contains(key)) {
                        checkedGenerators.put(key, map.get(key));
					}
				}

				if (checkedGenerators.size() > 0) {
                    for (Map.Entry<String, ICodeGenerator> entry : map.entrySet()) {
                        if (((AbstractCodeGenerator) entry.getValue()).isHidden()) {
                            checkedGenerators.put(entry.getKey(), entry.getValue());
                        }
                    }
				    
					statusLine.setText("生成中...");
					for (String key : checkedGenerators.keySet()) {
						statusLine.append("\n"+ key + "进行中...");
						ICodeGenerator generator = checkedGenerators.get(key);
						generator.generate();
						statusLine.append("\n"+ key + "完成");
					}
				} else {
					statusLine.setText("请选择生成器");
				}

			} catch (Exception e) {
				statusLine.append("\nERROR:" + e.getMessage());
				e.printStackTrace();
			}
		});
		btnPanel.add(ok);

		JButton close = new JButton("关闭");
		close.addActionListener((e) -> {
			frame.setVisible(false);
			frame.dispose();
		});
		btnPanel.add(close);
		
		root.add(btnPanel);

		statusLine = new JTextArea("准备就绪", 5, 0);
		statusLine.setLineWrap(true);
		JScrollPane status = new JScrollPane(statusLine);
		status.setBackground(Color.LIGHT_GRAY);

		JSeparator statusSpt = new JSeparator(SwingConstants.HORIZONTAL);
		root.add(statusSpt);
		root.add(status);
		
		frame.add(root);
		frame.pack();
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		double d1 = Toolkit.getDefaultToolkit().getScreenSize().getWidth();
		double d2 = Toolkit.getDefaultToolkit().getScreenSize().getHeight();
		frame.setLocation((int) (d1 - frame.getWidth()) / 2, (int) (d2 - frame.getHeight()) / 2);

		frame.setVisible(true);
	}
	
	public void buildModelProviderGui(JPanel root, ApplicationContext ctx) {
		try {
			JPanel providerPanel = new JPanel();
			
			IModelProvider modelProvider = ctx.getBean(IModelProvider.class);
			if (modelProvider instanceof CachedModelProvider) {
				modelProvider = ((CachedModelProvider) modelProvider).getModelProvider();
			}
			if (modelProvider instanceof DatabaseModelProvider) {
				DatabaseModelProvider dbProvider = (DatabaseModelProvider) modelProvider;

				providerPanel.setLayout(new BoxLayout(providerPanel, BoxLayout.PAGE_AXIS));

				JPanel catalogPanel = new JPanel();
				catalogPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
				JLabel catalogLabel = new JLabel("catalog:");
				catalogPanel.add(catalogLabel);
				JTextField catalogText = new JTextField(dbProvider.getCatalog(), 50);
				catalogText.addFocusListener(new FocusListener() {
					@Override
					public void focusLost(FocusEvent e) {
						dbProvider.setCatalog(catalogText.getText());
					}

					@Override
					public void focusGained(FocusEvent e) {
					}
				});
				catalogPanel.add(catalogText);
				providerPanel.add(catalogPanel);

				JPanel schemaPanel = new JPanel();
				schemaPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
				JLabel schemaLabel = new JLabel("schemaPattern:");
				schemaPanel.add(schemaLabel);
				JTextField schemaText = new JTextField(dbProvider.getSchemaPattern(), 50);
				schemaText.addFocusListener(new FocusListener() {
					@Override
					public void focusLost(FocusEvent e) {
						dbProvider.setSchemaPattern(schemaText.getText());
					}

					@Override
					public void focusGained(FocusEvent e) {}
				});
				schemaPanel.add(schemaText);
				providerPanel.add(schemaPanel);

				JPanel tablePanel = new JPanel();
				tablePanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
				JLabel tableLabel = new JLabel("*tablePattern:");
				tablePanel.add(tableLabel);
				JTextField tableText = new JTextField(dbProvider.getTablePattern(), 50);
				tableText.addFocusListener(new FocusListener() {
					@Override
					public void focusLost(FocusEvent e) {
						dbProvider.setTablePattern(tableText.getText());
					}

					@Override
					public void focusGained(FocusEvent e) {}
				});
				tablePanel.add(tableText);
				providerPanel.add(tablePanel);

				JPanel columnPanel = new JPanel();
				columnPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
				JLabel columnLabel = new JLabel("*columnPattern:");
				columnPanel.add(columnLabel);
				JTextField columnText = new JTextField(dbProvider.getColumnPattern(), 50);
				columnText.addFocusListener(new FocusListener() {
					@Override
					public void focusLost(FocusEvent e) {
						dbProvider.setColumnPattern(columnText.getText());
					}

					@Override
					public void focusGained(FocusEvent e) {}
				});
				columnPanel.add(columnText);
				providerPanel.add(columnPanel);
				
				root.add(providerPanel);
			}
		} catch (NoSuchBeanDefinitionException ignore) {
			// do nothing
		}
	}

	private static List<String> buildCodeGeneratorGui(JPanel root, Map<String, ICodeGenerator> map) {
		List<String> enableGenerators = Lists.newArrayList();
		JPanel codeGeneratorPanel = new JPanel();

		JCheckBox checkAll = new JCheckBox("全选", false);
		checkAll.setBackground(Color.YELLOW);
		JLabel codeGeneratorLabel = new JLabel("*生成器:");
		codeGeneratorPanel.add(codeGeneratorLabel);
		codeGeneratorPanel.add(checkAll);
        
        JPanel boxPanel = new JPanel();
        boxPanel.setLayout(new BoxLayout(boxPanel, BoxLayout.Y_AXIS));
        List<JCheckBox> boxHolder = Lists.newArrayList();
        
        JPanel overridePanel = new JPanel();
        JPanel overrideGroup = null;
        
        JCheckBox overrideAll = new JCheckBox("全选", false);
        overrideAll.setBackground(Color.YELLOW);
        JLabel overrideLabel = new JLabel("覆盖文件:");
        overridePanel.add(overrideLabel);
        overridePanel.add(overrideAll);
        
        JPanel radioPanel = new JPanel();
        radioPanel.setLayout(new BoxLayout(radioPanel, BoxLayout.Y_AXIS));
        List<JRadioButton> radioHolder = Lists.newArrayList();
        
		int boxIdx = 0;
        int radioIdx = 0;
		JPanel boxGroup = null;
		for (String key : map.keySet()) {
			AbstractCodeGenerator cg = (AbstractCodeGenerator) map.get(key);
			if (boxIdx % 6 == 0) {
				boxGroup = new JPanel();
				boxGroup.setLayout(new FlowLayout(FlowLayout.LEFT));
				boxPanel.add(boxGroup);
			}
            if (radioIdx % 6 == 0) {
                overrideGroup = new JPanel();
                overrideGroup.setLayout(new FlowLayout(FlowLayout.LEFT));
                radioPanel.add(overrideGroup);
            }
            if (!cg.isHidden()) {
                JCheckBox box = new JCheckBox(cg.getName(), false);
                box.addItemListener(e -> {
                    if (e.getStateChange() == ItemEvent.SELECTED) {
                        enableGenerators.add(key);
                    } else if (e.getStateChange() == ItemEvent.DESELECTED) {
                        enableGenerators.remove(key);
                    }
                });
                boxGroup.add(box);
                boxIdx ++;
                boxHolder.add(box);
            }
            if (cg instanceof FileCodeGenerator) {
                FileCodeGenerator fcg = (FileCodeGenerator) cg;
                if (!fcg.isHidden() && fcg.isOverridable()) {
                    JRadioButton override = new JRadioButton(cg.getName(), fcg.isOverride());
                    override.addChangeListener(e -> {
                        JRadioButton b = (JRadioButton) e.getSource();
                        fcg.setOverride(b.isSelected());
                    });
                    overrideGroup.add(override);
                    radioHolder.add(override);
                    radioIdx ++;
                }
            }

		}

		checkAll.addItemListener(e -> {
			if (e.getStateChange() == ItemEvent.SELECTED) {
				for (JCheckBox box : boxHolder) {
					box.setSelected(true);
				}
			} else if (e.getStateChange() == ItemEvent.DESELECTED) {
				for (JCheckBox box : boxHolder) {
					box.setSelected(false);
				}
			}
		});
        
        overrideAll.addItemListener(e -> {
            if (e.getStateChange() == ItemEvent.SELECTED) {
                for (JRadioButton box : radioHolder) {
                    box.setSelected(true);
                }
            } else if (e.getStateChange() == ItemEvent.DESELECTED) {
                for (JRadioButton box : radioHolder) {
                    box.setSelected(false);
                }
            }
        });

		codeGeneratorPanel.add(boxPanel);
        overridePanel.add(radioPanel);
		root.add(codeGeneratorPanel);
        root.add(overridePanel);
		return enableGenerators;
	}
    
    public void setRenderModelProviderGui(boolean renderModelProviderGui) {
        this.renderModelProviderGui = renderModelProviderGui;
    }
}
